<script setup lang="ts">
import { ref, reactive } from 'vue';

// 类型定义
interface Component {
  id: string;
  x: number;
  y: number;
  w: number;
  h: number;
  content: string;
  color: string;
}

interface SnapLines {
  vertical: number | null;
  horizontal: number | null;
}

interface PositionInfo {
  x: number;
  y: number;
  w: number;
  h: number;
}

type ResizePoint = 't' | 'r' | 'b' | 'l' | 'lt' | 'rt' | 'lb' | 'rb';

// 响应式数据
const canvasRef = ref<HTMLElement>();
const components = ref<Component[]>([
  { id: '1', x: 100, y: 100, w: 120, h: 80, content: '组件 1', color: '#ff6b6b' },
  { id: '2', x: 300, y: 200, w: 150, h: 100, content: '组件 2', color: '#4ecdc4' },
  { id: '3', x: 200, y: 350, w: 100, h: 60, content: '组件 3', color: '#45b7d1' },
]);

const selectedId = ref<string | null>(null);
const isDragging = ref(false);
const isResizing = ref(false);
const gridEnabled = ref(true);
const snapEnabled = ref(true);
const gridSize = ref(20);
const snapThreshold = ref(10);

const snapLines = reactive<SnapLines>({
  vertical: null,
  horizontal: null,
});

const positionInfo = reactive<PositionInfo>({
  x: 0,
  y: 0,
  w: 0,
  h: 0,
});

const showPositionInfo = ref(false);
const positionInfoStyle = ref({});

// 缩放锚点定义
const resizePoints: ResizePoint[] = ['t', 'r', 'b', 'l', 'lt', 'rt', 'lb', 'rb'];
const cursorMap: Record<ResizePoint, string> = {
  t: 'n-resize',
  r: 'e-resize',
  b: 's-resize',
  l: 'w-resize',
  lt: 'nw-resize',
  rt: 'ne-resize',
  lb: 'sw-resize',
  rb: 'se-resize',
};

// 拖拽状态
const dragState = {
  startX: 0,
  startY: 0,
  startComponentX: 0,
  startComponentY: 0,
  startComponentW: 0,
  startComponentH: 0,
  currentPoint: '' as ResizePoint,
};

// 计算组件样式
function getComponentStyle(component: Component) {
  return {
    position: 'absolute' as const,
    left: `${component.x}px`,
    top: `${component.y}px`,
    width: `${component.w}px`,
    height: `${component.h}px`,
    zIndex: selectedId.value === component.id ? 1000 : 1,
  };
}

// 计算组件内容样式
function getComponentContentStyle(component: Component) {
  return {
    width: '100%',
    height: '100%',
    backgroundColor: component.color,
    border: '2px solid #333',
    borderRadius: '8px',
    display: 'flex' as const,
    alignItems: 'center' as const,
    justifyContent: 'center' as const,
    fontSize: '14px',
    fontWeight: 'bold',
    color: '#fff',
    userSelect: 'none' as const,
    cursor: isDragging.value ? 'grabbing' : 'grab',
  };
}

// 计算缩放锚点样式
function getResizePointStyle(point: ResizePoint, component: Component) {
  const isTop = /t/.test(point);
  const isBottom = /b/.test(point);
  const isLeft = /l/.test(point);
  const isRight = /r/.test(point);

  let left = 0;
  let top = 0;

  // 计算锚点位置
  if (point.length === 2) {
    // 角点
    left = isLeft ? 0 : component.w;
    top = isTop ? 0 : component.h;
  } else {
    // 边点
    if (isTop || isBottom) {
      left = component.w / 2;
      top = isTop ? 0 : component.h;
    }
    if (isLeft || isRight) {
      left = isLeft ? 0 : component.w;
      top = component.h / 2;
    }
  }

  return {
    position: 'absolute' as const,
    left: `${left}px`,
    top: `${top}px`,
    cursor: cursorMap[point],
  };
}

// 吸附计算
function snapToGrid(value: number): number {
  if (!snapEnabled.value) {
    return value;
  }
  return Math.round(value / gridSize.value) * gridSize.value;
}

function findSnapLines(component: Component, excludeId: string) {
  if (!snapEnabled.value) {
    return { x: component.x, y: component.y };
  }

  const threshold = snapThreshold.value;
  let snapX = component.x;
  let snapY = component.y;

  snapLines.vertical = null;
  snapLines.horizontal = null;

  // 检查与其他组件的对齐
  components.value.forEach(other => {
    if (other.id === excludeId) {
      return;
    }

    // 垂直对齐检查
    const leftDiff = Math.abs(component.x - other.x);
    const rightDiff = Math.abs(component.x + component.w - (other.x + other.w));
    const centerDiff = Math.abs(component.x + component.w / 2 - (other.x + other.w / 2));

    if (leftDiff < threshold) {
      snapX = other.x;
      snapLines.vertical = other.x;
    } else if (rightDiff < threshold) {
      snapX = other.x + other.w - component.w;
      snapLines.vertical = other.x + other.w;
    } else if (centerDiff < threshold) {
      snapX = other.x + other.w / 2 - component.w / 2;
      snapLines.vertical = other.x + other.w / 2;
    }

    // 水平对齐检查
    const topDiff = Math.abs(component.y - other.y);
    const bottomDiff = Math.abs(component.y + component.h - (other.y + other.h));
    const middleDiff = Math.abs(component.y + component.h / 2 - (other.y + other.h / 2));

    if (topDiff < threshold) {
      snapY = other.y;
      snapLines.horizontal = other.y;
    } else if (bottomDiff < threshold) {
      snapY = other.y + other.h - component.h;
      snapLines.horizontal = other.y + other.h;
    } else if (middleDiff < threshold) {
      snapY = other.y + other.h / 2 - component.h / 2;
      snapLines.horizontal = other.y + other.h / 2;
    }
  });

  return { x: snapX, y: snapY };
}

// 更新位置信息显示
function updatePositionInfo(component: Component, mouseX: number, mouseY: number) {
  positionInfo.x = component.x;
  positionInfo.y = component.y;
  positionInfo.w = component.w;
  positionInfo.h = component.h;

  positionInfoStyle.value = {
    left: `${mouseX + 10}px`,
    top: `${mouseY - 40}px`,
  };

  showPositionInfo.value = true;
}

// 画布鼠标按下事件
function handleCanvasMouseDown(e: MouseEvent) {
  if (e.target === canvasRef.value) {
    selectedId.value = null;
  }
}

// 组件鼠标按下事件（拖拽）
function handleComponentMouseDown(e: MouseEvent, component: Component) {
  e.stopPropagation();
  selectedId.value = component.id;
  isDragging.value = true;

  dragState.startX = e.clientX;
  dragState.startY = e.clientY;
  dragState.startComponentX = component.x;
  dragState.startComponentY = component.y;

  const handleMouseMove = (e: MouseEvent) => {
    const deltaX = e.clientX - dragState.startX;
    const deltaY = e.clientY - dragState.startY;

    let newX = dragState.startComponentX + deltaX;
    let newY = dragState.startComponentY + deltaY;

    // 栅格吸附
    if (snapEnabled.value && gridEnabled.value) {
      newX = snapToGrid(newX);
      newY = snapToGrid(newY);
    }

    // 边界检查
    newX = Math.max(0, Math.min(newX, (canvasRef.value?.clientWidth || 800) - component.w));
    newY = Math.max(0, Math.min(newY, (canvasRef.value?.clientHeight || 600) - component.h));

    // 组件对齐吸附
    const snapped = findSnapLines({ ...component, x: newX, y: newY }, component.id);
    component.x = snapped.x;
    component.y = snapped.y;

    updatePositionInfo(component, e.clientX, e.clientY);
  };

  const handleMouseUp = () => {
    isDragging.value = false;
    showPositionInfo.value = false;
    snapLines.vertical = null;
    snapLines.horizontal = null;
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  };

  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);
}

// 缩放锚点鼠标按下事件
function handleResizeMouseDown(e: MouseEvent, point: ResizePoint, component: Component) {
  e.stopPropagation();
  isResizing.value = true;

  dragState.startX = e.clientX;
  dragState.startY = e.clientY;
  dragState.startComponentX = component.x;
  dragState.startComponentY = component.y;
  dragState.startComponentW = component.w;
  dragState.startComponentH = component.h;
  dragState.currentPoint = point;

  const handleMouseMove = (e: MouseEvent) => {
    const deltaX = e.clientX - dragState.startX;
    const deltaY = e.clientY - dragState.startY;

    const isTop = /t/.test(point);
    const isBottom = /b/.test(point);
    const isLeft = /l/.test(point);
    const isRight = /r/.test(point);

    // 计算新的尺寸和位置
    let newW = dragState.startComponentW;
    let newH = dragState.startComponentH;
    let newX = dragState.startComponentX;
    let newY = dragState.startComponentY;

    if (isLeft) {
      newW = dragState.startComponentW - deltaX;
      newX = dragState.startComponentX + deltaX;
    } else if (isRight) {
      newW = dragState.startComponentW + deltaX;
    }

    if (isTop) {
      newH = dragState.startComponentH - deltaY;
      newY = dragState.startComponentY + deltaY;
    } else if (isBottom) {
      newH = dragState.startComponentH + deltaY;
    }

    // 最小尺寸限制
    const minSize = 20;
    if (newW < minSize) {
      newW = minSize;
      if (isLeft) {
        newX = dragState.startComponentX + dragState.startComponentW - minSize;
      }
    }
    if (newH < minSize) {
      newH = minSize;
      if (isTop) {
        newY = dragState.startComponentY + dragState.startComponentH - minSize;
      }
    }

    // 栅格吸附
    if (snapEnabled.value && gridEnabled.value) {
      newW = snapToGrid(newW);
      newH = snapToGrid(newH);
      newX = snapToGrid(newX);
      newY = snapToGrid(newY);
    }

    // 边界检查
    const canvasWidth = canvasRef.value?.clientWidth || 800;
    const canvasHeight = canvasRef.value?.clientHeight || 600;

    if (newX + newW > canvasWidth) {
      if (isRight) {
        newW = canvasWidth - newX;
      } else if (isLeft) {
        newX = canvasWidth - newW;
        if (newX < 0) {
          newX = 0;
          newW = canvasWidth;
        }
      }
    }

    if (newY + newH > canvasHeight) {
      if (isBottom) {
        newH = canvasHeight - newY;
      } else if (isTop) {
        newY = canvasHeight - newH;
        if (newY < 0) {
          newY = 0;
          newH = canvasHeight;
        }
      }
    }

    // 应用变更
    component.w = newW;
    component.h = newH;
    component.x = newX;
    component.y = newY;

    updatePositionInfo(component, e.clientX, e.clientY);
  };

  const handleMouseUp = () => {
    isResizing.value = false;
    showPositionInfo.value = false;
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  };

  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);
}

// 添加组件
function addComponent() {
  const newComponent: Component = {
    id: Date.now().toString(),
    x: Math.random() * 300 + 50,
    y: Math.random() * 200 + 50,
    w: 120,
    h: 80,
    content: `组件 ${components.value.length + 1}`,
    color: `hsl(${Math.random() * 360}, 70%, 60%)`,
  };
  components.value.push(newComponent);
  selectedId.value = newComponent.id;
}

// 清空画布
function clearAll() {
  components.value = [];
  selectedId.value = null;
}
</script>

<template>
  <div class="canvas-editor">
    <!-- 控制面板 -->
    <div class="control-panel">
      <div class="control-group">
        <label>
          <input v-model="gridEnabled" type="checkbox" /> 显示栅格
        </label>
        <label>
          <input v-model="snapEnabled" type="checkbox" /> 启用吸附
        </label>
      </div>
      <div class="control-group">
        <label>
          栅格大小: <input v-model="gridSize" type="range" min="10" max="50" />
          {{ gridSize }}px
        </label>
        <label>
          吸附敏感度: <input v-model="snapThreshold" type="range" min="5" max="20" />
          {{ snapThreshold }}px
        </label>
      </div>
      <div class="control-group">
        <button @click="addComponent">
          添加组件
        </button>
        <button @click="clearAll">
          清空画布
        </button>
      </div>
    </div>

    <!-- 画布容器 -->
    <div
      ref="canvasRef"
      class="canvas-container"
      :style="{
        backgroundImage: gridEnabled ? `
          linear-gradient(to right, #e0e0e0 1px, transparent 1px),
          linear-gradient(to bottom, #e0e0e0 1px, transparent 1px)
        ` : 'none',
        backgroundSize: gridEnabled ? `${gridSize}px ${gridSize}px` : 'auto',
      }"
      @mousedown="handleCanvasMouseDown"
    >
      <!-- 组件渲染 -->
      <div
        v-for="component of components"
        :key="component.id"
        class="component-wrapper"
        :class="{
          selected: selectedId === component.id,
          dragging: isDragging && selectedId === component.id,
        }"
        :style="getComponentStyle(component)"
        @mousedown="handleComponentMouseDown($event, component)"
      >
        <!-- 组件内容 -->
        <div class="component-content" :style="getComponentContentStyle(component)">
          {{ component.content }}
        </div>

        <!-- 缩放锚点 -->
        <template v-if="selectedId === component.id && !isDragging">
          <div
            v-for="point of resizePoints"
            :key="point"
            :class="`resize-point ${point}`"
            :style="getResizePointStyle(point, component)"
            @mousedown="handleResizeMouseDown($event, point, component)"
          />
        </template>

        <!-- 选中边框 -->
        <div v-if="selectedId === component.id" class="selection-border" />
      </div>

      <!-- 吸附辅助线 -->
      <div v-if="snapLines.vertical !== null" class="snap-line vertical" :style="{ left: `${snapLines.vertical}px` }" />
      <div v-if="snapLines.horizontal !== null" class="snap-line horizontal" :style="{ top: `${snapLines.horizontal}px` }" />

      <!-- 位置信息显示 -->
      <div v-if="showPositionInfo" class="position-info" :style="positionInfoStyle">
        X: {{ Math.round(positionInfo.x) }}, Y: {{ Math.round(positionInfo.y) }}<br />
        W: {{ Math.round(positionInfo.w) }}, H: {{ Math.round(positionInfo.h) }}
      </div>
    </div>
  </div>
</template>

<style scoped>
.canvas-editor {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: Arial, sans-serif;
}

.control-panel {
  background: #f5f5f5;
  padding: 15px;
  border-bottom: 1px solid #ddd;
  display: flex;
  gap: 20px;
  align-items: center;
  flex-wrap: wrap;
}

.control-group {
  display: flex;
  gap: 15px;
  align-items: center;
}

.control-group label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 14px;
}

.control-group input[type="range"] {
  width: 80px;
}

.control-group button {
  padding: 8px 16px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.control-group button:hover {
  background: #0056b3;
}

.canvas-container {
  flex: 1;
  position: relative;
  background: #fff;
  overflow: hidden;
  user-select: none;
}

.component-wrapper {
  position: absolute;
  transition: none;
}

.component-wrapper.selected {
  z-index: 1000 !important;
}

.component-wrapper.dragging {
  transition: none;
}

.component-content {
  position: relative;
  z-index: 1;
}

.selection-border {
  position: absolute;
  top: -2px;
  left: -2px;
  right: -2px;
  bottom: -2px;
  border: 2px solid #007bff;
  border-radius: 8px;
  pointer-events: none;
  z-index: 2;
}

.resize-point {
  position: absolute;
  width: 8px;
  height: 8px;
  background: #007bff;
  border: 2px solid #fff;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  box-shadow: 0 0 3px rgba(0,0,0,0.3);
}

.resize-point:hover {
  background: #0056b3;
  transform: translate(-50%, -50%) scale(1.2);
}

/* 边点样式调整 */
.resize-point.t,
.resize-point.b {
  width: 20px;
  height: 8px;
  border-radius: 4px;
}

.resize-point.l,
.resize-point.r {
  width: 8px;
  height: 20px;
  border-radius: 4px;
}

.snap-line {
  position: absolute;
  background: #ff4757;
  pointer-events: none;
  z-index: 999;
}

.snap-line.vertical {
  width: 1px;
  height: 100%;
  top: 0;
}

.snap-line.horizontal {
  height: 1px;
  width: 100%;
  left: 0;
}

.position-info {
  position: absolute;
  background: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 8px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-family: monospace;
  pointer-events: none;
  z-index: 1001;
  white-space: nowrap;
}
</style>
